package com.esdk.utils;

import cn.hutool.http.HttpUtil;
import cn.hutool.log.Log;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.esdk.esdk;
import lombok.NonNull;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

public class SendLog{
  private Log _logger;
  private static PlumelogAsyncRunable plumeLogAsyncRunable;
  private static String plumelogSendUrl=esdk.prop.get("plumelog_send_url");

  public SendLog(){}

  public SendLog(@NonNull Log logger){
    this._logger = logger;
  }

  public void info(String v,Object... args){
    PlumeLogData plumeLogData=new PlumeLogData(esdk.str.format(v,args),"INFO",1);
    if(_logger!=null)
      _logger.info(plumeLogData.formatContent());
    asyncToDb(plumeLogData);
  }

  public void warn(String v,Object... args){
    PlumeLogData plumeLogData=new PlumeLogData(esdk.str.format(v,args),"WARN",1);
    if(_logger!=null)
      _logger.warn(plumeLogData.formatContent());
    asyncToDb(plumeLogData);
  }
  public void error(String v,Object... args){
    PlumeLogData plumeLogData=new PlumeLogData(esdk.str.format(v,args),"ERROR",2);
    if(_logger!=null)
      _logger.error(plumeLogData.formatContent());
    asyncToDb(plumeLogData);
  }

  public void error(Throwable e){
    PlumeLogData plumeLogData=new PlumeLogData(esdk.tool.getExceptionStackTrace(e),"ERROR",2);
    if(_logger!=null)
      _logger.error(plumeLogData.formatContent());
    asyncToDb(plumeLogData);
  }

  protected void asyncToDb(@NonNull PlumeLogData logData){
    if(plumeLogAsyncRunable==null&&esdk.str.isValid(plumelogSendUrl)){
      plumeLogAsyncRunable=new PlumelogAsyncRunable();
      new Thread(plumeLogAsyncRunable,"plumeLogAsync").start();
    }
    if(plumeLogAsyncRunable!=null)
      plumeLogAsyncRunable.append(logData);
  }

  static public class PlumeLogData {
    public String appName,serverName,content,logLevel,caller,logType;
    public Date logTime;
    public long traceCode;
    private StackTraceElement stack;

    public PlumeLogData(String msg,String level,int type){
      appName=esdk.prop.getString("project.name");
      serverName=esdk.net.LocalIp;
      content=esdk.str.ellipsis(msg,2000);
      logLevel=level;
      logTime=new Date();
      traceCode=com.esdk.sql.orm.ConnectionUtils.getRequestId();
      stack=esdk.tool.getStackTrace(4);
      caller=formatCaller("{C}.{M}:{L}");
      logType=type+"";
    }
    public String formatContent(){
      return formatCaller()+" "+content;
    }
    public String formatCaller(){
      String callerPattern="WARN|ERROR".contains(logLevel)?esdk.tool.ErrorCallerTemplate:esdk.tool.InfoCallerTemplate;
      return formatCaller(callerPattern);
    }
    public String formatCaller(String callerPattern){
      return esdk.str.format(callerPattern,Map.of("C",esdk.str.getClassShortName(stack.getClassName()),"M",stack.getMethodName(),"L",stack.getLineNumber(),"F",stack.getFileName()));
    }
  }

  private class PlumelogAsyncRunable implements Runnable{
    ConcurrentLinkedQueue<PlumeLogData> rowList=new ConcurrentLinkedQueue();
    @Override
    public void run(){
      while(true){
        try{
          while(!rowList.isEmpty()){
            List<PlumeLogData> copy=esdk.array.pollAll(rowList);
            JSONArray data=esdk.json.toJsonArray(copy);
            JSONObject jo=new JSONObject();
            jo.put("data",data);
            Response response=new Response(HttpUtil.post(plumelogSendUrl,jo.toString()));
            esdk.tool.IF(!response.success(),()->esdk.error(response.getMsg()),()->esdk.sout(response.getMsg()));
          }
        }catch(Exception e){
          esdk.error(e);
        }
        esdk.tool.sleep(1000);
      }
    }

    void append(PlumeLogData data){
      rowList.add(data);
    }

  }
}
